package com.xiaoyang.conf;

import com.xiaoyang.bean.Car;
import com.xiaoyang.bean.Pet;
import com.xiaoyang.bean.User;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;

/**
 * @author lichenyang
 * @create 2021-07-11 15:09
 *
 *
 * full模式: 配置类组件之间有依赖关系, 方法被调用得到之前单实例组件, 用Full模式
 *
 * lite模式: 配置类组件之间无依赖关系使用 lite 模式加速容器启动过程, 减少判断
 *
 * @Configuration: 告诉spring 这是一个配置类, 配置类本身也是一个组件
 * proxyBeanMethods: 允许通过配置类中的直接方法调用以及外部调用此配置的 {@code @Bean} 方法
 *  true:
 *      MyConfig: com.xiaoyang.conf.MyConfig$$EnhancerBySpringCGLIB$$9070cb46@44b29496: 从容器中获取到的MyConfig对象为 被spring cglib 增强了的代理对象, 此对象在获取组件的时候 先查询IOC容器中是否存在, 如果存在则直接返回
 *      MyConfig 内部的组件:
 *          如果组件在容器中已经存在, 则直接从容器中获取
 *
 *  false:
 *      MyConfig: com.xiaoyang.conf.MyConfig@29a98d9f: 从容器中获取到的 MyConfig 对象就是一个普通的对象
 *      MyConfig 内部的组件:
 *          不管容器中是否存在, 都会重新创建(就是一个普通的方法然后里面 new 一个对象)
 * 什么时候用true: 当创建容器的时候, 配置类中的组件互相依赖, 就需要使用true
 * 什么时候用false: 当创建容器的时候, 配置类中的组件没有互相依赖, 你的就是你的, 我的就是我的, 就可以使用false, 可以加快容器的启动速度
 * 为什么可以加快容器的启动速度: 因为 如果使用 true模式的话, 代理对象还会去容器中寻找当前需要创建的组件是否存在, 而false 直接就 new 一个新的
 *
 * @Configuration 中使用 @Bean 创建的对象 默认是单实例的
 *
 *  @Bean: 给容器中添加组件, 以方法名作为组件的id, 返回类型就是组件的类型, 返回的值就是容器中的实例
 *  name属性: 表示注册到 单例池中的容器的名称
 *
 * @Import()
 *  val: class 数组
 *  位置: 任何组件或者配置类
 *  作用: 使用无参数构造器创建 class 的对象放到容器中, 容器中class 的名称是 类的全类名
 *
 * @EnableConfigurationProperties(Car.class):
 *  功能1. 开启 car 的配置绑定功能
 *  功能2. 将 Car 这个组件自动注册到 Spring 容器中
 */
//@Import(User.class)
@Configuration(proxyBeanMethods = true)
//@ImportResource("classpath:bean.xml")
@EnableConfigurationProperties(Car.class)
public class MyConfig {

    /**
     * 无论外部调用多少次这个组件注册的方法, 获取的都是之前注册到容器中的单实例对象
     * @return
     */
//    @Bean
    public User user(){
        User user = new User("张三", 18);
        /*
        //当 proxyBeanMethods = true 的时候, 可以调用 this.pet() 方法, 因为调用 pet() 方法的时候 不会生成新的 pet 对象 可以确定 确实在上下文中注入了相应的 bean
        //当 proxyBeanMethods = false 的时候 IDEA 下面会有下划线 为什么呢: 因为 每次调用 pet()方法都会创建一个新的 Pet 对象, 这样就无法确定 新创建的那个Pet对象就是我想要注入的那个对象
        Pet pet = this.pet();
        System.out.println(pet);
        user.setPet();
        */
        return user;
    }

//    @Bean(name = "zhangsandemao")
    @ConditionalOnBean(name = "user")
    @Bean()
    public Pet pet(){
        return new Pet("张三的猫");
    }

}
